using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Annotations;
using Kaleida.ServiceMonitor.Framework;
using System.Linq;

namespace Kaleida.ServiceMonitor.Model
{
	/// <summary>
	/// A list with an upper size and age limit.
	/// The list is designed for a few items (approx 100)
	/// </summary>
	/// <typeparam name="T"></typeparam>
    public class LimitedList<T>
	{
		private ITimeProvider timeProvider = new SystemTimeProvider();
        private readonly string name;
        private readonly int maximumItemCount;
        [CanBeNull]private readonly Duration maximumAge;
		private readonly List<Timestamped<T>> items = new List<Timestamped<T>>();

		public LimitedList(string name, int maximumItemCount)
			: this(name, maximumItemCount, null)
		{
		}

        public LimitedList(string name, int maximumItemCount, Duration maximumAge)
        {
            this.name = name;
            this.maximumItemCount = maximumItemCount;
			this.maximumAge = maximumAge;
        }

		internal void SetTimeProvider(ITimeProvider newTimeProvider)
		{
			timeProvider = newTimeProvider;
		}

		public void Clear()
		{
			lock (items)
			{
				items.Clear();
			}
		}

        public void AddToTop(T request)
        {
            lock (items)
            {
				items.Insert(0, new Timestamped<T>(timeProvider.GetCurrent(), request));

            	EvictItemsBasedOnNumber();
            	EvictItemsBasedOnAge();
            }
        }

		private void EvictItemsBasedOnNumber()
		{
			if (items.Count > maximumItemCount)
			{
				items.RemoveAt(items.Count - 1);
			}
		}

		private void EvictItemsBasedOnAge()
		{
            if (maximumAge != null)
            {
                var now = timeProvider.GetCurrent();
                items.RemoveAll(i => now - i.DateTime > maximumAge.ToTimeSpan());
            }
		}

		public string Name
        {
            get { return name; }
        }

        public int MaximumItemCount
        {
            get { return maximumItemCount; }
        }

	    public IList<T> GetItemsNewestFirst()
	    {
	        lock (items)
	        {
	            EvictItemsBasedOnAge();
	            return items.Select(i => i.Item).ToList();
	        }
	    }

        [CanBeNull]
	    public T OldestItemOrDefault
        {
            get { return GetItemsNewestFirst().LastOrDefault(); }
        }

        [CanBeNull]
        public T NewestItemOrDefault
        {
            get { return GetItemsNewestFirst().FirstOrDefault(); }
        }

		public int ItemCount
		{
			get
			{
				lock (items)
				{
					return items.Count;
				}
			}
		}

        public string Summary()
        {
            var contentSummary = new StringBuilder();
            contentSummary.AppendFormat("Last {0} ", MaximumItemCount);
            contentSummary.Append(Name);
            contentSummary.Append(MaximumItemCount == 1 ? "" : "s");

            if (maximumAge != null)
            {
                contentSummary.AppendFormat(" (max age {0})", maximumAge);
            }

            return contentSummary.ToString();
        }
    }

	internal class Timestamped<T>
	{
		private readonly DateTime dateTime;
		private readonly T item;

		internal Timestamped(DateTime dateTime, T item)
		{
			this.dateTime = dateTime;
			this.item = item;
		}

		internal DateTime DateTime
		{
			get { return dateTime; }
		}

		internal T Item
		{
			get { return item; }
		}
	}

}